home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / fs / fsInit.c < prev    next >
C/C++ Source or Header  |  1991-07-17  |  17KB  |  628 lines

  1. /* 
  2.  * fsInit.c --
  3.  *
  4.  *    Filesystem initializtion.  This is done by setting up the
  5.  *    initial process and then having all other processes
  6.  *    inherit things.
  7.  *
  8.  * Copyright (C) 1987 Regents of the University of California
  9.  * All rights reserved.
  10.  * Permission to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose and without
  12.  * fee is hereby granted, provided that the above copyright
  13.  * notice appear in all copies.  The University of California
  14.  * makes no representations about the suitability of this
  15.  * software for any purpose.  It is provided "as is" without
  16.  * express or implied warranty.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /sprite/src/kernel/fs/RCS/fsInit.c,v 9.15 91/07/17 11:48:59 jhh Exp $ SPRITE (Berkeley)";
  21. #endif not lint
  22.  
  23.  
  24. #include <sprite.h>
  25.  
  26. #include <fs.h>
  27. #include <fsutil.h>
  28. #include <fsNameOps.h>
  29. #include <fsio.h>
  30. #include <fsprefix.h>
  31. #include <fslcl.h>
  32. #include <devFsOpTable.h>
  33. #include <fspdev.h>
  34. #include <fsutilTrace.h>
  35. #include <fsStat.h>
  36. #include <fsconsist.h>
  37. #include <proc.h>
  38. #include <rpc.h>
  39. #include <recov.h>
  40. #include <timer.h>
  41. #include <trace.h>
  42. #include <fsdm.h>
  43. #include <fsrmt.h>
  44. #include <devTypes.h>
  45. #include <strings.h>
  46. #include <stdlib.h>
  47. #include <stdio.h>
  48.  
  49.  
  50. #define    SCSI_MAKE_DEVICE_TYPE(type, hbaType, ctrlNum, targetID, LUN, dBits) \
  51.         (((hbaType)<<8)|(type))
  52. #define    SCSI_MAKE_DEVICE_UNIT(type, hbaType, ctrlNum, targetID, LUN, dBits)  \
  53.         (((ctrlNum)<<10)|((LUN)<<7)|((targetID)<<4)|(dBits))
  54.  
  55. /*
  56.  * The prefix under which the local disk is attached.  
  57.  */
  58. #define LOCAL_DISK_NAME        "/"
  59. int fsDefaultDomainNumber = 0;
  60.  
  61. /*
  62.  * We record the maximum transfer size supported by the RPC system
  63.  * for use in chopping up remote I/O operations.
  64.  *
  65.  * If these are initialized to zero they will be set to the maximum
  66.  * sizes reported back by the RPC system.
  67.  */
  68. int fsMaxRpcDataSize = 0;    /* Used to be 4096 */
  69. int fsMaxRpcParamSize = 0;    /* Used to be 1024 */
  70.  
  71. /*
  72.  * Statistics structure.
  73.  */
  74. Fs_Stats    fs_Stats;
  75.  
  76. /*
  77.  * Timer queue element for updating time of day.
  78.  */
  79.  
  80. Timer_QueueElement    fsutil_TimeOfDayElement;
  81.  
  82. /* 
  83.  * Flag to make sure we only do certain things, such as syncing the disks,
  84.  * after the file system has been initialized.
  85.  */
  86. Boolean fsutil_Initialized = FALSE;
  87.  
  88. /*
  89.  * Flag to indicate whether we have attached a disk or not.  We can use
  90.  * this to determine if we are a client or a server.
  91.  */
  92.  
  93. Boolean fsDiskAttached = FALSE;
  94.  
  95.  
  96. void Fs_Init()
  97. {
  98.     Fs_InitData();
  99.     Fs_InitNameSpace();
  100. }
  101. /*
  102.  *----------------------------------------------------------------------
  103.  *
  104.  * Fs_InitData
  105.  *
  106.  *    Initialize most filesystem data structures.
  107.  *
  108.  * Results:
  109.  *    None.
  110.  *
  111.  * Side effects:
  112.  *    Data structures in the file system are initialized.
  113.  *
  114.  *----------------------------------------------------------------------
  115.  */
  116. void
  117. Fs_InitData()
  118. {
  119.     /*
  120.      * Initialized the known domains. Local, Remote, and Pfs.
  121.      */
  122.     Fslcl_NameInitializeOps();
  123.     Fsio_InitializeOps();
  124.     Fsrmt_InitializeOps();
  125.     Fspdev_InitializeOps();
  126.  
  127.     bzero((Address) &fs_Stats, sizeof(Fs_Stats));
  128.     fs_Stats.statsVersion = FS_STAT_VERSION;
  129.  
  130.     /*
  131.      * The handle cache and the block cache start out with a hash table of
  132.      * a given size which grows on demand.  Thus the numbers passed to
  133.      * the next two routines are not crucial.
  134.      */
  135.     Fscache_Init(64);
  136.     Fsutil_HandleInit(64);
  137.  
  138.     Fsprefix_Init();
  139.  
  140.     Fsconsist_ClientInit();
  141.  
  142.     Fslcl_DomainInit();
  143.  
  144.     Fsutil_TraceInit();
  145.     Fspdev_TraceInit();
  146. }
  147.  
  148.  
  149. /*
  150.  *----------------------------------------------------------------------
  151.  *
  152.  * Fs_InitNameSpace
  153.  *
  154.  *    Initialize the filesystem name space.
  155.  *    This does the first steps in boot-strapping
  156.  *    the name space.  The prefix table is primed with "/", and
  157.  *    a local disk is attached under "/bootTmp" if possible.  Later
  158.  *    in Fs_ProcInit "/bootTmp" gets promoted to root if noone
  159.  *    else is around to serve root.
  160.  *
  161.  *    This also initializes the file system's time (which could
  162.  *    perhaps be replaced someday).  This has to be done after
  163.  *    Rpc_Start.
  164.  *
  165.  * Results:
  166.  *    None.
  167.  *
  168.  * Side effects:
  169.  *    "/" is stuck in the prefix table with no handle.
  170.  *    Local disk is attached under "/bootTmp".
  171.  *
  172.  *----------------------------------------------------------------------
  173.  */
  174. void
  175. Fs_InitNameSpace()
  176. {
  177.  
  178.     /*
  179.      * Install a crash callback with the recovery module.  (A reboot
  180.      * callback is installed later only if we have to recover with a host.)
  181.      */
  182.  
  183.     Recov_CrashRegister(Fsutil_ClientCrashed, (ClientData) NIL);
  184.  
  185.     /*
  186.      * This is the initial step in boot-strapping the name space.  Place
  187.      * an entry for "/" in the prefix table.  The NIL token will cause a
  188.      * broadcast to get a valid token the first time the prefix is used.
  189.      */
  190.     (void)Fsprefix_Install("/", (Fs_HandleHeader *)NIL, -1, FSPREFIX_IMPORTED); 
  191.  
  192.     fsutil_Initialized = TRUE;
  193. }
  194.  
  195.  
  196. /*
  197.  *----------------------------------------------------------------------
  198.  *
  199.  * Fs_Bin --
  200.  *
  201.  *    Setup objects to be binned.
  202.  *
  203.  * Results:
  204.  *    None.
  205.  *
  206.  * Side effects:
  207.  *    None.
  208.  *
  209.  *----------------------------------------------------------------------
  210.  */
  211. void
  212. Fs_Bin()
  213. {
  214.     Fsio_Bin();
  215.     Fspdev_Bin();
  216.     Fsrmt_Bin();
  217. #ifdef INET
  218.     FsSocketBin();
  219. #endif
  220.     Mem_Bin(FS_BLOCK_SIZE);
  221. }
  222.  
  223.  
  224. /*
  225.  *----------------------------------------------------------------------
  226.  *
  227.  * Fs_ProcInit
  228.  *
  229.  *    Initialize the filesystem part of the process table entry.
  230.  *    This has to be called after Proc_InitMainProc
  231.  *    because of the call to Proc_GetCurrentProc.
  232.  *    It should be done after Rpc_Start because it might do
  233.  *    an RPC to open the current directory.
  234.  *
  235.  * Results:
  236.  *    None.
  237.  *
  238.  * Side effects:
  239.  *    The current directory is opened for the main process.
  240.  *    The permissions mask is initialized.
  241.  *    The table of open file Id's is cleared to zero size.
  242.  *
  243.  *----------------------------------------------------------------------
  244.  */
  245. void
  246. Fs_ProcInit()
  247. {
  248.     ReturnStatus    status;        /* General status code return */
  249.     Proc_ControlBlock    *procPtr;    /* Main process's proc table entry */
  250.     register Fs_ProcessState    *fsPtr;    /* FS state ref'ed from proc table */
  251.     Fs_Stream        *stream;
  252.     Fs_Device         *defaultDiskPtr;
  253.     int            i;
  254.     int            argc;
  255.     char        *argv[10];
  256.     char        argBuffer[256];
  257.     int            numDefaults;
  258.     Boolean        stdDefaults;
  259.     Time        waitTime;
  260.     Time        incrTime;
  261.  
  262.     procPtr = Proc_GetCurrentProc();
  263.     procPtr->fsPtr = fsPtr = mnew(Fs_ProcessState);
  264.     /*
  265.      * General filesystem initialization.
  266.      * Find out how much we can transfer with the RPC system.
  267.      * (If these are already set we don't change them.)
  268.      *
  269.      * FIX THIS TO CALL DOMAIN SPECIFIC INITIALIZATION ROUTINES.
  270.      */
  271.     if (fsMaxRpcDataSize + fsMaxRpcParamSize == 0) {
  272.     Rpc_MaxSizes(&fsMaxRpcDataSize, &fsMaxRpcParamSize);
  273.     }
  274.  
  275.     fsPtr->numGroupIDs    = 1;
  276.     fsPtr->groupIDs     = (int *) malloc(1 * sizeof(int));
  277.     fsPtr->groupIDs[0]    = 0;
  278.  
  279.     defaultDiskPtr = (Fs_Device *) malloc(sizeof(Fs_Device));
  280.     numDefaults = devNumDefaultDiskPartitions;
  281.     stdDefaults = TRUE;
  282.     argc = Mach_GetBootArgs(10, 256, argv, argBuffer);
  283.     for (i = 0; i < argc; i++) {
  284.     if (!strcasecmp(argv[i], "-rootdisk")) {
  285.         if (argc == i + 1) {
  286.         printf("-rootdisk option requires an argument\n");
  287.         } else {
  288.         int    type;
  289.         int    unit;
  290.         int    n;
  291.         n = sscanf(argv[i+1], " %d.%d ", &type, &unit);
  292.         if (n != 2) {
  293.             printf("-rootdisk has the following syntax:\n");
  294.             printf("\t-rootdisk <type>.<unit>\n");
  295.         } else {
  296.             printf("Found -rootdisk option, %d.%d\n",
  297.             type, unit);
  298.             defaultDiskPtr->serverID = -1;
  299.             defaultDiskPtr->type = type;
  300.             defaultDiskPtr->unit = unit;
  301.             defaultDiskPtr->data = (ClientData) NIL;
  302.             numDefaults = 1;
  303.             stdDefaults = FALSE;
  304.         }
  305.         }
  306.     }
  307.     }
  308.     for (i=0 ; i< numDefaults ; i++) {
  309.     /*
  310.      * Second step in bootstrapping the name space.  Try and attach
  311.      * a disk. 
  312.      */
  313.     if (stdDefaults) {
  314.         *defaultDiskPtr = devFsDefaultDiskPartitions[i];
  315.     }
  316.  
  317.     status = Fsdm_AttachDisk(defaultDiskPtr, LOCAL_DISK_NAME, 
  318.             FS_ATTACH_LOCAL | FS_DEFAULT_DOMAIN);
  319.     if (status == SUCCESS) {
  320.         Fs_Attributes    attr;
  321.         int            i;
  322.         char        buffer[128];
  323.         Boolean        rootServer = FALSE;
  324.  
  325.         sprintf(buffer, "%s/ROOT", LOCAL_DISK_NAME);
  326.         status = Fs_GetAttributes(buffer, FS_ATTRIB_FILE, &attr);
  327.         if (status != SUCCESS) {
  328.         printf("Stat of %s returned 0x%x\n", buffer, status);
  329.         rootServer = FALSE;
  330.         } else {
  331.         printf("Found %s.\n", buffer);
  332.         rootServer = TRUE;
  333.         }
  334.         argc = Mach_GetBootArgs(10, 256, argv, argBuffer);
  335.         for (i = 0; i < argc; i++) {
  336.         if (!strcasecmp(argv[i], "-backup")) {
  337.             printf("Found %s option.\n", argv[i]);
  338.             rootServer = FALSE;
  339.         }
  340.         if (!strcasecmp(argv[i], "-root")) {
  341.             printf("Found %s option.\n", argv[i]);
  342.             rootServer = TRUE;
  343.         }
  344.         }
  345.         if (rootServer) {
  346.         sprintf(buffer, "%s/boot", LOCAL_DISK_NAME);
  347.         status = Fs_GetAttributes(buffer, FS_ATTRIB_FILE, &attr);
  348.         if (status != SUCCESS) {
  349.             printf("/boot not found on root partition.\n");
  350.             rootServer = FALSE;
  351.         } else {
  352.             printf("Found /boot.\n");
  353.             if ((attr.type != FS_DIRECTORY)) {
  354.             printf("/boot is not a directory!\n");
  355.             rootServer = FALSE;
  356.             } else {
  357.             printf("/boot is a directory.\n");
  358.             }
  359.         }
  360.         }
  361.         if (rootServer) {
  362.         Fs_Stream        *streamPtr;
  363.         static char        rootPrefix[] = "/";
  364.     
  365.         status = Fs_Open(LOCAL_DISK_NAME, FS_READ|FS_FOLLOW,
  366.                 FS_DIRECTORY, 0, &streamPtr);
  367.         if (status != SUCCESS) {
  368.             printf("Fs_ProcInit: Unable to open local disk prefix!");
  369.         } else {
  370.             printf("Installing the local disk as %s.\n", rootPrefix);
  371.             (void)Fsprefix_Install(rootPrefix, streamPtr->ioHandlePtr, 
  372.                 FS_LOCAL_DOMAIN, 
  373.                 FSPREFIX_LOCAL|FSPREFIX_IMPORTED|FSPREFIX_OVERRIDE);
  374.             if (strcmp(LOCAL_DISK_NAME, rootPrefix)) {
  375.             printf("Clearing local disk prefix %s.\n", 
  376.                 LOCAL_DISK_NAME);
  377.             Fsprefix_Clear(LOCAL_DISK_NAME, TRUE, FALSE);
  378.             }
  379.             fsDiskAttached = TRUE;
  380.             break;
  381.         }
  382.         } else {
  383.         printf("Detaching the local disk.\n");
  384.         Fsdm_DetachDisk(LOCAL_DISK_NAME);
  385.         }
  386.     }
  387.     }
  388.     /*
  389.      * Try and open /.
  390.      */
  391.     status = FAILURE;
  392.     Time_Multiply(time_OneSecond, 5, &incrTime);
  393.     waitTime = time_ZeroSeconds;
  394.     do {
  395.     if (Time_LT(waitTime, time_OneMinute)) {
  396.         Time_Add(waitTime, incrTime, &waitTime);
  397.     }
  398.     status = Fs_Open("/", FS_READ, FS_DIRECTORY, 0, &stream);
  399.     if (status != SUCCESS) {
  400.         if (fsDiskAttached) {
  401.         panic(
  402.         "Fs_ProcInit: I'm the root server and I can't open \"/\".\n");
  403.         }
  404.         /*
  405.          *  Wait a bit and retry the open of "/".
  406.          */
  407.         printf("Can't find server for \"/\", waiting %d seconds.\n",
  408.         waitTime.seconds);
  409.         (void)Sync_WaitTime(waitTime);
  410.     }
  411.     } while (status != SUCCESS);
  412.     Fs_Close(stream);
  413.     fsPtr->cwdPtr = (Fs_Stream *)NIL;
  414.     status = Fs_Open("/boot", FS_READ, FS_DIRECTORY, 0, &fsPtr->cwdPtr);
  415.     if (status != SUCCESS) {
  416.     panic("Fs_ProcInit: can't open /boot.\n");
  417.     }
  418.     /*
  419.      * Set the default permissions mask; it indicates the maximal set of
  420.      * permissions that a newly created file can have.
  421.      */
  422.     fsPtr->filePermissions = FS_OWNER_WRITE |
  423.             FS_OWNER_READ | FS_OWNER_EXEC |
  424.             FS_GROUP_READ | FS_GROUP_EXEC |
  425.             FS_WORLD_READ | FS_WORLD_EXEC;
  426.     /*
  427.      * The open file list is only needed by user processes.
  428.      * Fs_GetNewID will create and grow this list.
  429.      */
  430.     fsPtr->numStreams = 0;
  431.     fsPtr->streamList = (Fs_Stream **)NIL;
  432.     if (!fsDiskAttached) {
  433.     free((char *) defaultDiskPtr);
  434.     }
  435.     return;
  436. }
  437.  
  438.  
  439. /*
  440.  *----------------------------------------------------------------------
  441.  *
  442.  * Fs_InheritState -
  443.  *
  444.  *    This is called during process creation to have the new process
  445.  *    inherit filesystem state from its parent.  This includes the
  446.  *    current directory, open files, and the permission mask.
  447.  *
  448.  * Results:
  449.  *    None.
  450.  *
  451.  * Side effects:
  452.  *    Duplicate the filesystem state of the parent process in
  453.  *    the new child process.
  454.  *
  455.  *----------------------------------------------------------------------
  456.  */
  457. void
  458. Fs_InheritState(parentProcPtr, newProcPtr)
  459.     Proc_ControlBlock *parentProcPtr;    /* Process to inherit from */
  460.     Proc_ControlBlock *newProcPtr;    /* Process that inherits */
  461. {
  462.     register     Fs_ProcessState    *parFsPtr;
  463.     register     Fs_ProcessState    *newFsPtr;
  464.     register    Fs_Stream    **parStreamPtrPtr;
  465.     register    Fs_Stream    **newStreamPtrPtr;
  466.     register     int         i;
  467.     int             len;
  468.  
  469.     /*
  470.      * User Ids.  This is kept separate from the file system state
  471.      * so other modules can use the user ID for permission checking.
  472.      */
  473.     newProcPtr->userID = parentProcPtr->userID;
  474.  
  475.     /*
  476.      * The rest of the filesystem state hangs off the Fs_ProcessState record.
  477.      * This could be shared after forking to implement file descriptor sharing.
  478.      */
  479.     parFsPtr = parentProcPtr->fsPtr;
  480.     newProcPtr->fsPtr = newFsPtr = mnew(Fs_ProcessState);
  481.  
  482.     /*
  483.      * Current working directory.
  484.      */
  485.     if (parFsPtr->cwdPtr != (Fs_Stream *)NIL) {
  486.     Fsio_StreamCopy(parFsPtr->cwdPtr, &newFsPtr->cwdPtr);
  487.     } else {
  488.     newFsPtr->cwdPtr = (Fs_Stream *)NIL;
  489.     }
  490.  
  491.     /*
  492.      * Open stream list.
  493.      */
  494.     len = newFsPtr->numStreams = parFsPtr->numStreams;
  495.     if (len > 0) {
  496.     newFsPtr->streamList =
  497.         (Fs_Stream **)malloc(len * sizeof(Fs_Stream *));
  498.     newFsPtr->streamFlags = (char *)malloc(len * sizeof(char));
  499.     for (i = 0, parStreamPtrPtr = parFsPtr->streamList,
  500.             newStreamPtrPtr = newFsPtr->streamList;
  501.          i < len;
  502.          i++, parStreamPtrPtr++, newStreamPtrPtr++) {
  503.         if (*parStreamPtrPtr != (Fs_Stream *) NIL) {
  504.         Fsio_StreamCopy(*parStreamPtrPtr, newStreamPtrPtr);
  505.         newFsPtr->streamFlags[i] = parFsPtr->streamFlags[i];
  506.         } else {
  507.         *newStreamPtrPtr = (Fs_Stream *) NIL;
  508.         newFsPtr->streamFlags[i] = 0;
  509.         }
  510.     }
  511.     } else {
  512.     newFsPtr->streamList = (Fs_Stream **)NIL;
  513.     newFsPtr->streamFlags = (char *)NIL;
  514.     }
  515.  
  516.     newFsPtr->filePermissions = parFsPtr->filePermissions;
  517.  
  518.     /*
  519.      * Group ID list.
  520.      */
  521.  
  522.     len = newFsPtr->numGroupIDs = parFsPtr->numGroupIDs;
  523.     if (len) {
  524.     newFsPtr->groupIDs = (int *)malloc(len * sizeof(int));
  525.     for (i=0 ; i<len; i++) {
  526.         newFsPtr->groupIDs[i] = parFsPtr->groupIDs[i];
  527.     }
  528.     } else {
  529.     newFsPtr->groupIDs = (int *)NIL;
  530.     }
  531. }
  532.  
  533. /*
  534.  *----------------------------------------------------------------------
  535.  *
  536.  * Fs_CloseOnExec -
  537.  *
  538.  *    Called just before a process overlays itself with another program.
  539.  *    At this point any stream marked as CLOSE_ON_EXEC are closed.
  540.  *    This makes cleanup for shell-like programs easier.
  541.  *
  542.  * Results:
  543.  *    None.
  544.  *
  545.  * Side effects:
  546.  *    Close streams marked CLOSE_ON_EXEC.
  547.  *
  548.  *----------------------------------------------------------------------
  549.  */
  550. void
  551. Fs_CloseOnExec(procPtr)
  552.     Proc_ControlBlock *procPtr;        /* Process to operate on */
  553. {
  554.     register int i;
  555.     register Fs_Stream **streamPtrPtr;    /* Pointer into streamList */
  556.     char *flagPtr;            /* Pointer into flagList */
  557.  
  558.     for (i=0, streamPtrPtr = procPtr->fsPtr->streamList,
  559.           flagPtr = procPtr->fsPtr->streamFlags ;
  560.      i < procPtr->fsPtr->numStreams ;
  561.      i++, streamPtrPtr++, flagPtr++) {
  562.     if ((*streamPtrPtr != (Fs_Stream *)NIL) &&
  563.         (*flagPtr & FS_CLOSE_ON_EXEC)) {
  564.         (void)Fs_Close(*streamPtrPtr);
  565.         *streamPtrPtr = (Fs_Stream *)NIL;
  566.     }
  567.     }
  568. }
  569.  
  570. /*
  571.  *----------------------------------------------------------------------
  572.  *
  573.  * Fs_CloseState
  574.  *
  575.  *    This is called when a process dies to clean up its filesystem state
  576.  *    by closing its open files and its current directory.
  577.  *
  578.  *    Note: to avoid problems when destroying a process, we have two
  579.  *    phases of removing file system state.  Phase 0 will just close
  580.  *    streams.  Phase 1 closes down everything.  (Phase 0 is optional.)
  581.  *
  582.  * Results:
  583.  *    None.
  584.  *
  585.  * Side effects:
  586.  *    All its open streams are closed, and associated memory is free'd.
  587.  *
  588.  *----------------------------------------------------------------------
  589.  */
  590. void
  591. Fs_CloseState(procPtr, phase)
  592.     Proc_ControlBlock *procPtr;        /* An exiting process to clean up */
  593.     int phase;                /* 0 for start, 1 for total. */
  594. {
  595.     register int i;
  596.     register Fs_ProcessState *fsPtr = procPtr->fsPtr;
  597.  
  598.     if (phase>0) {
  599.     if (fsPtr->cwdPtr != (Fs_Stream *) NIL) {
  600.         (void)Fs_Close(fsPtr->cwdPtr);
  601.     }
  602.     }
  603.  
  604.     if (fsPtr->streamList != (Fs_Stream **)NIL) {
  605.     for (i=0 ; i < fsPtr->numStreams ; i++) {
  606.         register Fs_Stream *streamPtr;
  607.  
  608.         streamPtr = fsPtr->streamList[i];
  609.         if (streamPtr != (Fs_Stream *)NIL) {
  610.         (void)Fs_Close(streamPtr);
  611.         }
  612.     }
  613.     free((Address) fsPtr->streamList);
  614.     free((Address) fsPtr->streamFlags);
  615.     fsPtr->streamList = (Fs_Stream **)NIL;
  616.     }
  617.  
  618.     if (phase>0) {
  619.     if (fsPtr->groupIDs != (int *) NIL) {
  620.         free((Address) fsPtr->groupIDs);
  621.         fsPtr->groupIDs = (int *) NIL;
  622.         fsPtr->numGroupIDs = 0;
  623.     }
  624.     free((Address)fsPtr);
  625.     procPtr->fsPtr = (Fs_ProcessState *)NIL;
  626.     }
  627. }
  628.